home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung (Tewi)(1994).iso
/
magazine
/
msysjour
/
vol06
/
03
/
winedd
/
driver.c
< prev
next >
Wrap
Text File
|
1991-05-01
|
12KB
|
382 lines
/* ----------------------------- DRIVER.C ----------------------------------
Embedded Device Driver Application.
Device Driver module.
Spark Software Inc. 1991.
INTHANDLER - The interrupt service routine.
DevOpen - Opens the pseudodevice.
DevClose - Closes the pseudodevice.
DevRead - Reads the pseudodevice.
GetNumUnreadItems - Returns the number of register sets not yet read.
GetDevError - Returns and resets device status information.
------------------------------------------------------------------------- */
/* The following is an intentionally empty definition; this is used to tell */
/* the compiler that we wish to have global variables declared in this */
/* source module and only defined in all others. */
#define PUBLIC
#include <windows.h>
#include <dos.h>
#include "devdefs.h"
#include "intnum.h"
#include "dprivate.h"
#include "device.h"
/* Public function prototypes. */
PUBLIC VOID FAR PASCAL WEP( int ); /* Called by the kernel on DLL exit. */
/* Private data object declarations. */
PRIVATE VOID (interrupt FAR *lpOldHandler)( );/* Address of the old handler. */
PRIVATE BOOL bWrapped; /* Have we wrapped around the buffer? */
PRIVATE WORD wNumDevErrors; /* Number of errors while processing ints. */
PRIVATE WORD wDevError; /* Type of errors while processing ints. */
PRIVATE int nUnreadItems; /* Number of register sets not yet read. */
PRIVATE HWND hWndDevUser; /* Handle of window that opened the device. */
PRIVATE REGISTERS InterruptData[ MAX_INTERRUPTS ]; /* Saved registers. */
PRIVATE PREGISTERS pIntRegisterSet; /* Modified by INTHANDLER( ). */
PRIVATE PREGISTERS pReadRegisterSet; /* Modified by DevRead( ). */
PRIVATE union REGS Regs; /* For setting up interrupt vector. */
PRIVATE struct SREGS SRegs; /* " */
PUBLIC VOID FAR INTERRUPT_ATTRIBUTE INTHANDLER( _es , _ds , _di , _si , _bp , _sp , _bx , _dx , _cx , _ax , _ip , _cs , _flags )
unsigned _es;
unsigned _ds;
unsigned _di;
unsigned _si;
unsigned _bp;
unsigned _sp;
unsigned _bx;
unsigned _dx;
unsigned _cx;
unsigned _ax;
unsigned _ip;
unsigned _cs;
unsigned _flags;
{
BOOL bLocalWrapped;
PREGISTERS pLocalIntRegisterSet;
PREGISTERS pLocalReadRegisterSet;
/* Copy data segment information into the stack. */
pLocalIntRegisterSet = pIntRegisterSet;
pLocalReadRegisterSet = pReadRegisterSet;
bLocalWrapped = bWrapped;
/* Prepare for next time IMMEDIATELY to avoid reentrancy problems. */
nUnreadItems++;
/* Need to wrap? */
if( ++pIntRegisterSet >= InterruptData + MAX_INTERRUPTS )
{
/* Wrap around to the beginning of the buffer for next time. */
bWrapped = TRUE;
pIntRegisterSet = InterruptData;
}
/* For now, we're using stack variables; it's OK to turn interrupts on. */
_enable( );
/* Buffer the interrupt data. */
/* NOTE: we're doing it the boring way, as opposed to arranging the */
/* elements of REGISTERS to be the same as the argument list, then doing */
/* the assignment *pLocalIntRegisterSet = *(PREGISTERS)&es. That can */
/* be done, but it is both arcane and possibly dependent on the compiler */
/* version: should the arglist change in future versions of the compiler,*/
/* that code might break. */
pLocalIntRegisterSet->ax = _ax;
pLocalIntRegisterSet->bx = _bx;
pLocalIntRegisterSet->cx = _cx;
pLocalIntRegisterSet->dx = _dx;
pLocalIntRegisterSet->cs = _cs;
pLocalIntRegisterSet->ds = _ds;
pLocalIntRegisterSet->es = _es;
pLocalIntRegisterSet->si = _si;
pLocalIntRegisterSet->di = _di;
pLocalIntRegisterSet->bp = _bp;
pLocalIntRegisterSet->sp = _sp;
pLocalIntRegisterSet->ip = _ip;
pLocalIntRegisterSet->flags = _flags;
/* Check for buffer overrun, (new data overwriting old unread data). */
if( bLocalWrapped && pLocalIntRegisterSet > pLocalReadRegisterSet )
{
/* Changing data segment vars. */
_disable( );
/* Log the error. */
wDevError |= DEVERROR_OVERRUN;
wNumDevErrors++;
/* Only using stack variables. */
_enable( );
}
/* NOTE: interrupts MUST be left on when exiting an ISR. */
}/* INTHANDLER( ) */
PUBLIC BOOL FAR PASCAL DevOpen( hWnd )
HWND hWnd;
{
/* Is the device in use? */
if( hWndDevUser )
{
/* Device already in use. */
wDevError |= DEVERROR_NOTOWNER;
wNumDevErrors++;
return FALSE;
}
/* If the device is a hardware device, we should check for its */
/* existence, possibly using inp( ), inpw( ), outp( ) or outpw( ) */
/* to check an I/O port for an expected value. This code goes here. */
/* Set up our buffering. */
pIntRegisterSet = pReadRegisterSet = InterruptData;
/* Save the handle of the calling window. */
hWndDevUser = hWnd;
/* Say that we have no device errors. */
wNumDevErrors = wDevError = 0;
/* Tell any readers that we're not wrapped yet. */
bWrapped = FALSE;
/* Get the old ISR's address. */
Regs.h.ah = DOSSVC_GETVECT;
Regs.h.al = INTNUM;
intdosx( &Regs , &Regs , &SRegs );
FP_SEG( lpOldHandler ) = SRegs.es;
FP_OFF( lpOldHandler ) = Regs.x.bx;
/* Install the new interrupt handler. */
Regs.h.ah = DOSSVC_SETVECT;
Regs.h.al = INTNUM;
SRegs.ds = HIWORD( (DWORD)INTHANDLER );
Regs.x.dx = LOWORD( (DWORD)INTHANDLER );
intdosx( &Regs , &Regs , &SRegs );
return TRUE;
}/* DevOpen( ) */
PUBLIC BOOL FAR PASCAL DevClose( hWnd )
HWND hWnd;
{
/* Is this the device user? */
if( hWnd != hWndDevUser )
{
/* This user does not own the device. Call the device police. */
wDevError |= DEVERROR_NOTOWNER;
wNumDevErrors++;
return FALSE;
}
/* If the device is a hardware device, the port commands */
/* necessary to shut down the device go here. */
/* Uninstall the interrupt handler. */
Regs.h.ah = DOSSVC_SETVECT;
Regs.h.al = INTNUM;
SRegs.ds = HIWORD( (DWORD)lpOldHandler );
Regs.x.dx = LOWORD( (DWORD)lpOldHandler );
intdosx( &Regs , &Regs , &SRegs );
/* Say that hWnd is no longer using this device. */
hWndDevUser = (HWND)NULL;
return TRUE;
}/* DevClose( ) */
PUBLIC WORD FAR PASCAL DevRead( hWnd , lpRegisters , wItemsRequested )
HWND hWnd;
register LPREGISTERS lpRegisters;
register WORD wItemsRequested;
{
BOOL bBreak;
register WORD wItemsRead;
/* Is this the device user? */
if( hWnd != hWndDevUser )
{
/* No. */
wDevError |= DEVERROR_NOTOWNER;
wNumDevErrors++;
return 0;
}
/* Check for new data. */
wItemsRead = 0;
bBreak = FALSE;
_disable( );
if( pReadRegisterSet == pIntRegisterSet && !bWrapped )
{
/* No new data. */
bBreak = TRUE;
}
_enable( );
/* Copy each item requested to the user's buffer; */
/* check for previous errors on each iteration. */
/* NOTE: this code is written in such a way that interrupts are allowed */
/* to sneak in during the JMP instruction that gets us back to the top */
/* of the loop and the instructions used to evaluate the while( ) */
/* condition. This allows us to protect the integrity of vars. in the */
/* data segment AND allow interrupts to be speedily processed. In this */
/* section of the code, it doesn't pay to use stack variables (almost */
/* every line must reference a data segment variable). */
while( !bBreak && !wNumDevErrors && wItemsRequested )
{
/* Turn interrupts off for this iteration. */
_disable( );
/* Copy this register set. */
*lpRegisters = *pReadRegisterSet;
/* Prepare for next time. */
lpRegisters++ , pReadRegisterSet++ , wItemsRead++ , wItemsRequested-- , nUnreadItems--;
/* Need to wrap? */
if( pReadRegisterSet >= InterruptData + MAX_INTERRUPTS )
{
if( bWrapped )
{
/* Wrap around to the beginning of the buffer. */
bWrapped = FALSE;
pReadRegisterSet = InterruptData;
}
else
{
/* We've somehow read past the last saved register set. */
/* This is an internal inconsistency - log it and leave. */
wDevError |= DEVERROR_INTERNAL;
wNumDevErrors++;
}
}
/* Should we terminate the loop? */
if( pReadRegisterSet == pIntRegisterSet )
{
/* Break at the end of this iteration. */
bBreak = TRUE;
}
/* Turn interrupts back on. */
_enable( );
}
return wItemsRead;
}/* DevRead( ) */
PUBLIC int FAR PASCAL GetNumUnreadItems( hWnd )
HWND hWnd;
{
/* Is this the device user? */
if( hWnd != hWndDevUser )
{
/* No. */
/* Turn interrupts off for now. */
_disable( );
/* Record the error. */
wDevError |= DEVERROR_NOTOWNER;
wNumDevErrors++;
/* Turn interrupts back on. */
_enable( );
return -1;
}
/* Return the number of register sets not yet read. */
return nUnreadItems;
}/* GetNumUnreadItems( ) */
PUBLIC WORD FAR PASCAL GetDevError( hWnd , lpwDevError , bReset )
HWND hWnd;
LPWORD lpwDevError;
BOOL bReset;
{
WORD wNumDevErrorsCopy;
/* Is this the device user? */
if( hWnd != hWndDevUser )
{
/* No. */
if( lpwDevError )
{
/* Turn interrupts off for now. */
_disable( );
*lpwDevError = wDevError |= DEVERROR_NOTOWNER;
/* Turn interrupts back on. */
_enable( );
}
return ++wNumDevErrors;
}
/* Turn interrupts off for now. */
_disable( );
/* Copy the bitflags and number of device errors. */
if( lpwDevError )
{
*lpwDevError = wDevError;
}
wNumDevErrorsCopy = wNumDevErrors;
if( bReset )
{
wNumDevErrors = wDevError = 0;
}
/* Turn interrupts back on. */
_enable( );
return wNumDevErrorsCopy;
}/* GetDevError( ) */
PUBLIC VOID FAR PASCAL WEP( nExitType )
int nExitType;
{
/* If the device is a hardware device, the port commands */
/* necessary to shut down the device go here. */
switch( nExitType )
{
case WEP_SYSTEM_EXIT:
/* System shutdown in progress. Respond accordingly. */
return;
case WEP_FREE_DLL:
/* DLL use count is zero. Respond accordingly. */
return;
default:
/* Undefined value. Ignore it. */
/* NOTE: even if the kernel hasn't passed us a recognized */
/* nExitType, we should still have shut down the hardware */
/* device before entering this switch. */
return;
}
}/* WEP( ) */
/* EOF */